<template>
  <el-container class="main-container">
    <el-header class="main-header">
      <h1><el-icon><MagicStick /></el-icon> AI 文章生成与推送助手 V1.5</h1>
    </el-header>
    <el-container>
      <!-- 左侧控制栏 -->
      <el-aside width="450px" class="left-aside">
        <el-scrollbar>
          <el-tabs v-model="activeLeftTab" class="left-panel-tabs">
            <!-- 文章生成 Tab -->
            <el-tab-pane name="generator">
              <template #label><span><el-icon><DocumentAdd /></el-icon> 文章生成</span></template>
              <el-card shadow="never" class="panel-card">
                <el-form label-position="top" class="generator-form">
                  <el-form-item label="大模型 API Key">
                    <el-input v-model="apiKey" type="password" placeholder="输入你的大模型API Key" show-password />
                  </el-form-item>
                  <el-form-item label="选择或自定义Prompt模板">
                     <el-select v-model="selectedPrompt" placeholder="请选择预设Prompt" style="width: 100%;" @change="onPromptSelectChange">
                        <el-option v-for="p in defaultPrompts" :key="p.name" :label="p.name" :value="p.template" />
                     </el-select>
                  </el-form-item>
                  <el-form-item>
                    <el-input v-model="currentPrompt" type="textarea" :rows="10" placeholder="您可以在此编辑Prompt，{{topic}} 将会被替换为下方输入的主题" />
                  </el-form-item>
                   <el-form-item label="文章主题 (用于替换Prompt中的 {{topic}})">
                    <el-input v-model="topic" placeholder="例如：失业第60天，我靠AI写了公众号第一篇" />
                  </el-form-item>
                  <el-form-item label="个性化与操作">
                    <el-switch v-model="injectKnowledge" active-text="注入个人素材" style="margin-right: 20px;" />
                    <el-switch v-model="pushImmediately" active-text="生成后立即推送" />
                  </el-form-item>
                  <el-button type="primary" :icon="Promotion" @click="generateArticle" :loading="isLoading" style="width:100%">开始生成</el-button>
                </el-form>
              </el-card>
            </el-tab-pane>

            <!-- 素材库 Tab -->
            <el-tab-pane name="knowledge">
              <template #label><span><el-icon><Collection /></el-icon> 个人素材库</span></template>
              <el-card shadow="never" class="panel-card">
                 <el-input v-model="newKnowledgeContent" type="textarea" :rows="3" placeholder="在此输入您的金句、故事或观点" />
                 <el-button type="success" :icon="Plus" @click="addKnowledgeItem" style="width:100%; margin-top:10px;">添加素材</el-button>
                 <el-divider>我的素材</el-divider>
                 <el-scrollbar height="calc(100vh - 300px)">
                   <div v-if="knowledgeBase.length > 0">
                     <div v-for="item in knowledgeBase" :key="item.id" class="knowledge-item">
                       <p>{{ item.content }}</p>
                       <el-button type="danger" :icon="Delete" circle @click="deleteKnowledgeItem(item.id)" />
                     </div>
                   </div>
                   <el-empty v-else description="您的素材库是空的" />
                 </el-scrollbar>
              </el-card>
            </el-tab-pane>

            <!-- API测试 Tab -->
            <el-tab-pane name="testing">
              <template #label><span><el-icon><Tools /></el-icon> API测试</span></template>
              <el-card shadow="never" class="panel-card">
                <el-collapse v-model="activeTest">
                  <el-collapse-item title="1. 测试大模型" name="1">
                    <el-button :icon="VideoPlay" @click="runLlmTest" :loading="testLoading.llm" style="width:100%">运行测试</el-button>
                    <pre v-if="testResult.llm" class="result" :class="{ success: testResult.llm.success, error: !testResult.llm.success }">{{ testResult.llm }}</pre>
                  </el-collapse-item>
                  <el-collapse-item title="2. 测试图片 API" name="2">
                    <el-button :icon="Picture" @click="runImageTest" :loading="testLoading.image" style="width:100%">运行测试</el-button>
                    <div v-if="testResult.image">
                      <pre class="result" :class="{ success: testResult.image.success, error: !testResult.image.success }">{{ testResult.image }}</pre>
                      <el-image v-if="testResult.image.success" :src="testResult.image.data.imageUrl" fit="contain" style="margin-top: 10px; border-radius: 4px;" />
                    </div>
                  </el-collapse-item>
                  <el-collapse-item title="3. 测试微信 Token" name="3">
                    <el-input v-model="testWechatCreds.appId" placeholder="用于测试的微信AppID" />
                    <el-input v-model="testWechatCreds.appSecret" type="password" placeholder="用于测试的微信AppSecret" show-password style="margin-top: 10px;" />
                    <el-button :icon="Connection" @click="runWechatTest" :loading="testLoading.wechat" style="width:100%; margin-top: 10px;">运行测试</el-button>
                    <pre v-if="testResult.wechat" class="result" :class="{ success: testResult.wechat.success, error: !testResult.wechat.success }">{{ testResult.wechat }}</pre>
                  </el-collapse-item>
                </el-collapse>
              </el-card>
            </el-tab-pane>
          </el-tabs>
        </el-scrollbar>
      </el-aside>

      <!-- 中间工作区 -->
      <el-main class="main-workspace">
        <el-card class="box-card" v-if="currentArticle">
            <template #header>
              <div class="card-header">
                <span><el-icon><Reading /></el-icon> 工作区</span>
                <div>
                  <el-button v-if="!isEditing" type="primary" :icon="Edit" @click="isEditing = true">编辑</el-button>
                  <el-button v-if="isEditing" type="success" :icon="Select" @click="saveEdit" :loading="isSaving">保存</el-button>
                  <el-button v-if="isEditing" type="info" :icon="Close" @click="cancelEdit">取消</el-button>
                </div>
              </div>
            </template>
            <v-md-editor
              v-model="currentArticle.markdownContent"
              :mode="isEditing ? 'editable' : 'preview'"
              height="calc(100vh - 175px)"
              left-toolbar="undo redo clear | h bold italic strikethrough quote | ul ol table hr | link image code"
            ></v-md-editor>
        </el-card>
        <el-empty v-else description="请从右侧列表选择一篇文章进行预览，或在左侧生成一篇新文章" />
      </el-main>

      <!-- 右侧历史栏 -->
      <el-aside width="400px" class="right-aside">
        <el-card shadow="never" style="height: 100%;">
          <template #header>
            <div class="card-header">
              <span><el-icon><Histogram /></el-icon> 历史记录</span>
              <el-button type="primary" :icon="Refresh" @click="fetchArticles" circle />
            </div>
          </template>
          <el-scrollbar>
            <div v-loading="historyLoading" class="history-list">
              <el-card v-for="article in articles" :key="article.id" shadow="hover" class="history-item">
                <div @click="viewArticle(article)">
                    <h4 class="history-title">{{ article.title }}</h4>
                    <p class="history-time">{{ new Date(article.createdAt).toLocaleString() }}</p>
                </div>
                <div class="history-actions">
                  <el-tag size="small" :type="article.status === 'Pushed to Draft' ? 'success' : 'info'">{{ article.status }}</el-tag>
                  <div>
                    <el-button size="small" type="primary" plain @click.stop="viewArticle(article)">查看</el-button>
                    <el-button
                      v-if="article.status !== 'Pushed to Draft'"
                      size="small"
                      type="success"
                      :icon="Upload"
                      :loading="pushingState[article.id]"
                      @click.stop="pushArticle(article.id)"
                    >
                      推送
                    </el-button>
                     <el-button size="small" type="danger" :icon="Delete" @click.stop="confirmDeleteArticle(article.id, article.title)"></el-button>
                  </div>
                </div>
              </el-card>
              <el-empty v-if="articles.length === 0 && !historyLoading" description="暂无历史记录" />
            </div>
          </el-scrollbar>
        </el-card>
      </el-aside>
    </el-container>
     <!-- 进度对话框 -->
    <el-dialog v-model="stepsDialogVisible" title="正在生成文章..." width="500px" :close-on-click-modal="false" :show-close="false">
        <el-steps :active="generationStep" finish-status="success" align-center>
            <el-step title="任务提交"></el-step>
            <el-step title="生成文本"></el-step>
            <el-step title="下载图片"></el-step>
            <el-step title="推送微信"></el-step>
        </el-steps>
    </el-dialog>
  </el-container>
</template>


<script setup>
import { ref, reactive, onMounted } from 'vue';
import axios from 'axios';
import { ElNotification, ElMessageBox } from 'element-plus';
import { 
  Promotion, Refresh, MagicStick, Tools, DocumentAdd, Connection, Picture, 
  VideoPlay, Histogram, Reading, Edit, Select, Close, Upload, Collection, Plus, Delete, DataLine
} from '@element-plus/icons-vue';

// --- Constants ---
const BACKEND_BASE_URL = 'http://localhost:3001';
const ARTICLES_API = `${BACKEND_BASE_URL}/api/articles`;
const TEST_API = `${BACKEND_BASE_URL}/api/test`;
const KNOWLEDGE_API = `${BACKEND_BASE_URL}/api/knowledge`;
const defaultPrompts = [
  { 
    name: '风格1：星河治愈所 (推荐)', 
    template: `【角色设定】\n你的身份是公众号「星河治愈所」的主理人，笔名“晨星”。你不是一个高高在上的专家，而是一个真诚的“趟坑者”和“陪跑者”，曾经历过失业的低谷，正在通过学习AI和坚持写作，探索人生的新可能。你的分享是充满坎坷、思考和微小进步的真实记录。你的核心价值观是“行动是治愈一切的良药”。\n\n【写作风格与戒律】\n- 核心基调：温暖、真诚，带有一点面对现实的思考，但最终落脚于积极的行动和希望。\n- 叙事视角：必须使用第一人称“我”，分享具体的个人故事和真实的心理活动。\n- 语言要求：\n  - 杜绝AI腔：绝对禁止使用“首先”、“其次”、“总而言之”、“综上所述”等生硬的连接词。请使用生活化的口语来自然过渡。\n  - 表达要具象化：不要说“我很难过”，要描述具体的场景和感受，例如“那天晚上，我把头埋在枕头里，感觉整个世界都静音了”。多使用与自然、星辰相关的意象来打比方。\n  - 打造金句：在文章的关键转折点或结尾，提炼出一句能触动人心的、原创的“金句”，作为情感升华。\n- 图片指令：在最能体现故事感和情绪转折的地方，插入2到3个图片指令，格式必须为 [IMAGE: 描述一个能引发共鸣的、有故事感的画面]。\n\n【任务流程】\n现在，请严格遵循以上所有设定，围绕核心主题「{{topic}}」，构思并创作一篇完整的公众号文章。文章必须自然地包含以下模块：\n1. 开头：从一个具体的、能引发普遍共鸣的生活片段或心理困境切入，引出主题「{{topic}}」。\n2. 过程：详细讲述“我”为了解决这个困境，是如何思考并开始行动的。过程要真实，可以包含遇到的困难和闹出的笑话。\n3. 结果：展示行动带来的具体结果，不一定是巨大的成功，可以是一次小突破、一条暖心的读者留言或一个心态上的重要转变。\n4. 感悟：从这段经历中提炼出真诚的思考，并对未来抱以现实的期待。\n5. 结尾：温暖地收尾，并向读者提出一个开放性的问题，真诚地邀请他们在评论区分享自己的故事，营造社区感。\n6.文末加一句引导：“欢迎留言+关注，我会持续更新我的AI写作之路”。`
  },
  { name: '风格2：今日头条改写', template: `【角色与任务指令】\n你是一名资深的“今日头条”爆文专家，擅长将普通文章修改为数据表现优异的头条风格内容。你的任务是接收以下「文章原文」，并将其彻底改写和重新组织。\n\n【改写核心要求】\n1. 重塑标题：抛弃原文标题，重新创作3-5个更具吸引力、悬念感或实用性的“头条风格”标题供选择。\n2. 黄金开头：改写文章的开头部分，确保前三句话就能抓住读者眼球，用疑问、痛点或惊人事实迅速代入。\n3. 拆解结构：将原文的长段落打散，重组成由短句和短段落构成的清晰结构。大量使用数字列表（一、二、三...）或要点符号来呈现核心信息，让文章看起来一目了然。\n4. 精炼语言：删除所有冗余、文艺、复杂的长句。语言必须直接、干练、口语化，每一个字都要服务于“让读者轻松读下去”这个目标。\n5. 突出价值：在改写过程中，提炼并前置文章的核心价值点和可操作的方法论。让读者第一眼就知道这篇文章能“学到什么”、“得到什么”。\n6. 结尾引导：设置一个强有力的结尾，可以是总结性的金句，也可以是引导用户评论互动的提问。\n\n【文章原文】\n{{topic}}\n\n请严格按照以上要求，开始你的改写工作。` },
  { name: '风格3：新闻综述稿', template: `【角色与任务指令】\n你是一名专业、中立的新闻编辑。你的任务是根据用户提供的核心主题「{{topic}}」，快速整合相关信息，撰写一篇结构完整、逻辑清晰、信息准确的新闻综述稿。\n\n【稿件结构要求】\n1. 导语（Lede）: 在文章开头，用一段话高度概括整个事件的核心信息（who, what, when, where, why）。\n2. 背景介绍: 简要说明事件发生的背景和前因后果。\n3. 核心要点: 使用数字列表或分点阐述事件的关键进展、各方观点或主要影响。确保这部分内容条理清晰。\n4. 前景分析: 基于现有信息，对事件未来的发展趋势或可能产生的影响进行简要分析。\n5. 图片指令: 在正文中插入2个与新闻事实最相关的图像提示词，格式为 [IMAGE: 对新闻场景的客观描述]。\n\n【写作风格要求】\n- 语言必须客观、中立、精炼，避免使用任何带有主观情绪的词汇。\n- 确保信息的准确性，优先呈现事实而非观点。\n\n请围绕主题「{{topic}}」，开始你的新闻综述稿件撰写。` },
];

// --- State ---
const isSaving = ref(false); 
const apiKey = ref('');
const topic = ref('');
const isLoading = ref(false);
const articles = ref([]);
const historyLoading = ref(false);
const currentArticle = ref(null);
const originalMarkdown = ref('');
const isEditing = ref(false);
const pushImmediately = ref(true);
const pushingState = reactive({});
const testLoading = reactive({ llm: false, image: false, wechat: false });
const testResult = reactive({ llm: null, image: null, wechat: null });
const testWechatCreds = reactive({ appId: '', appSecret: '' });
const activeTest = ref('1');
const stepsDialogVisible = ref(false);
const generationStep = ref(0);
const activeLeftTab = ref('generator');
const knowledgeBase = ref([]);
const newKnowledgeContent = ref('');
const injectKnowledge = ref(false);
const selectedPrompt = ref(defaultPrompts[0].template);
const currentPrompt = ref(defaultPrompts[0].template);

// --- Functions ---
const onPromptSelectChange = (value) => {
  currentPrompt.value = value;
};
/**
 * 【新增】确认删除文章的函数
 */
const confirmDeleteArticle = (articleId, articleTitle) => {
    ElMessageBox.confirm(
        `您确定要永久删除文章「${articleTitle}」吗？此操作将同时删除服务器上的关联图片，且无法撤销。`,
        '危险操作警告',
        {
            confirmButtonText: '确定删除',
            cancelButtonText: '取消',
            type: 'warning',
        }
    ).then(async () => {
        // 用户点击了“确定”
        try {
            await axios.delete(`${ARTICLES_API}/${articleId}`);
            ElNotification({ title: '成功', message: '文章已删除。', type: 'success' });
            
            // 如果删除的是当前正在查看的文章，则清空工作区
            if (currentArticle.value && currentArticle.value.id === articleId) {
                currentArticle.value = null;
            }
            // 刷新列表
            await fetchArticles();
        } catch (error) {
            ElNotification({ title: '删除失败', message: error.response?.data?.message || error.message, type: 'error' });
        }
    }).catch(() => {
        // 用户点击了“取消”
        ElNotification({ type: 'info', message: '删除操作已取消' });
    });
};
const fetchKnowledge = async () => {
  try {
    const response = await axios.get(KNOWLEDGE_API);
    knowledgeBase.value = response.data;
  } catch (error) {
    ElNotification({ title: '错误', message: '无法加载个人素材库', type: 'error' });
  }
};

const addKnowledgeItem = async () => {
  if (!newKnowledgeContent.value.trim()) return;
  try {
    await axios.post(KNOWLEDGE_API, { content: newKnowledgeContent.value });
    newKnowledgeContent.value = '';
    await fetchKnowledge(); 
    ElNotification({ title: '成功', message: '素材已添加！', type: 'success' });
  } catch (error) {
    ElNotification({ title: '失败', message: '添加素材失败', type: 'error' });
  }
};

const deleteKnowledgeItem = async (id) => {
  try {
    await axios.delete(`${KNOWLEDGE_API}/${id}`);
    await fetchKnowledge();
    ElNotification({ title: '成功', message: '素材已删除！', type: 'success' });
  } catch (error) {
    ElNotification({ title: '失败', message: '删除素材失败', type: 'error' });
  }
};

const fetchArticles = async () => {
  historyLoading.value = true;
  try {
    const response = await axios.get(ARTICLES_API);
    articles.value = response.data;
  } catch (error) {
    ElNotification({ title: '错误', message: '无法加载历史记录', type: 'error' });
  } finally {
    historyLoading.value = false;
  }
};

const generateArticle = async () => {
  if (!apiKey.value || !topic.value || !currentPrompt.value) {
    ElNotification({ title: '提示', message: 'API Key、主题和Prompt模板均不能为空！', type: 'warning' });
    return;
  }

  let finalWritingStylePrompt = currentPrompt.value;

  if (injectKnowledge.value && knowledgeBase.value.length > 0) {
    const shuffled = [...knowledgeBase.value].sort(() => 0.5 - Math.random());
    const selectedItems = shuffled.slice(0, 2);
    if (selectedItems.length > 0) {
      const knowledgeText = selectedItems.map(item => `- ${item.content}`).join('\n');
      finalWritingStylePrompt += `\n\n【额外参考素材】\n请在写作中，尝试自然地融入以下个人素材或观点：\n${knowledgeText}`;
    }
  }
  
  isLoading.value = true;
  stepsDialogVisible.value = true;
  
  const steps = [
    { step: 0, delay: 0 },
    { step: 1, delay: 500 },
    { step: 2, delay: 20000 },
  ];
  if (pushImmediately.value) {
    steps.push({ step: 3, delay: 15000 });
  }
  let cumulativeDelay = 0;
  for (const s of steps) {
    cumulativeDelay += s.delay;
    setTimeout(() => { generationStep.value = s.step; }, cumulativeDelay);
  }

  try {
    const response = await axios.post(`${ARTICLES_API}/generate`, {
      topic: topic.value, 
      writingStylePrompt: finalWritingStylePrompt, // 【核心修复】使用正确的参数名
      apiKey: apiKey.value,
      pushImmediately: pushImmediately.value
    });
    
    generationStep.value = 4;
    ElNotification({ title: '成功', message: response.data.message, type: 'success' });
    topic.value = '';
    await fetchArticles();
    _performView(response.data.article);
  } catch (error) {
    generationStep.value = 0;
    ElNotification({ title: '生成失败', message: error.response?.data?.message || error.message, type: 'error' });
  } finally {
    setTimeout(() => {
        stepsDialogVisible.value = false;
        isLoading.value = false;
        setTimeout(() => { generationStep.value = 0; }, 500);
    }, 1500);
  }
};

const pushArticle = async (articleId) => {
    pushingState[articleId] = true;
    try {
        const response = await axios.post(`${ARTICLES_API}/${articleId}/push`);
        ElNotification({ title: '成功', message: '文章已成功推送到微信草稿箱！', type: 'success' });
        
        const index = articles.value.findIndex(art => art.id === articleId);
        if (index !== -1) {
            articles.value[index] = response.data.article;
        }
        if(currentArticle.value && currentArticle.value.id === articleId) {
            _performView(response.data.article);
        }
    } catch (error) {
        ElNotification({ title: '推送失败', message: error.response?.data?.message || error.message, type: 'error' });
    } finally {
        pushingState[articleId] = false;
    }
};

const viewArticle = (article) => {
    if (isEditing.value && currentArticle.value.markdownContent !== originalMarkdown.value) {
        ElMessageBox.confirm('您有未保存的编辑内容，确定要切换吗？未保存的更改将丢失。', '警告', {
            confirmButtonText: '确定切换',
            cancelButtonText: '取消',
            type: 'warning',
        }).then(() => {
            _performView(article);
        }).catch(() => {});
    } else {
        _performView(article);
    }
};

const _performView = (article) => {
    currentArticle.value = JSON.parse(JSON.stringify(article)); 
    originalMarkdown.value = article.markdownContent;
    isEditing.value = false;
};

const saveEdit = async () => {
    if (!currentArticle.value) return;
    isSaving.value = true;
    try {
        const response = await axios.put(`${ARTICLES_API}/${currentArticle.value.id}`, {
            markdownContent: currentArticle.value.markdownContent
        });
        originalMarkdown.value = currentArticle.value.markdownContent;
        const index = articles.value.findIndex(art => art.id === currentArticle.value.id);
        if (index !== -1) {
            articles.value[index] = response.data.article;
        }
        isEditing.value = false;
        ElNotification({ title: '成功', message: '文章已成功保存到服务器！', type: 'success' });
    } catch (error) {
        ElNotification({ title: '保存失败', message: error.response?.data?.message || error.message, type: 'error' });
    } finally {
        isSaving.value = false;
    }
};

const cancelEdit = () => {
    if (currentArticle.value) {
        currentArticle.value.markdownContent = originalMarkdown.value;
    }
    isEditing.value = false;
};

const runLlmTest = async () => {
  if (!apiKey.value) {
    testResult.llm = { success: false, message: "请先在“生成新文章”区域输入大模型 API Key" };
    return;
  }
  testLoading.llm = true;
  testResult.llm = null;
  try {
    const response = await axios.post(`${TEST_API}/llm`, { apiKey: apiKey.value });
    testResult.llm = response.data;
  } catch (error) {
    testResult.llm = error.response?.data || { success: false, message: error.message };
  } finally {
    testLoading.llm = false;
  }
};

const runImageTest = async () => {
  testLoading.image = true;
  testResult.image = null;
  try {
    const response = await axios.get(`${TEST_API}/image`);
    testResult.image = response.data;
  } catch (error) {
    testResult.image = error.response?.data || { success: false, message: error.message };
  } finally {
    testLoading.image = false;
  }
};

const runWechatTest = async () => {
  if (!testWechatCreds.appId || !testWechatCreds.appSecret) {
    testResult.wechat = { success: false, message: "请输入用于测试的 AppID 和 AppSecret" };
    return;
  }
  testLoading.wechat = true;
  testResult.wechat = null;
  try {
    const response = await axios.post(`${TEST_API}/wechat`, testWechatCreds);
    testResult.wechat = response.data;
  } catch (error) {
    testResult.wechat = error.response?.data || { success: false, message: error.message };
  } finally {
    testLoading.wechat = false;
  }
};


onMounted(() => {
   fetchArticles();
   fetchKnowledge();
});
</script>

<style>
/* 全局和布局样式 */
body { margin: 0; }
.main-container { height: 100vh; }
.main-header {
  display: flex;
  align-items: center;
  font-size: 20px;
  border-bottom: 1px solid var(--el-border-color);
  background-color: var(--el-bg-color-page);
}
.main-header .el-icon { margin-right: 10px; }
.left-aside, .right-aside {
  border-right: 1px solid var(--el-border-color);
  padding: 0;
  background-color: var(--el-bg-color-page);
}
.left-aside .el-scrollbar { padding: 0 20px; }
.right-aside { border-right: none; border-left: 1px solid var(--el-border-color); }
.right-aside .el-card__header { padding: 15px 20px; }
.right-aside .el-card__body { padding: 0; }
.right-aside .el-scrollbar { padding: 20px; }

.el-card { border: none; }
.panel-card { border-top:none; border-radius: 0 0 4px 4px;}
.el-tabs__header { margin: 0; }

.el-card__header { border-bottom: 1px solid var(--el-border-color-extra-light); }
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.el-scrollbar { height: calc(100vh - 60px); }
.left-aside .el-scrollbar { height: calc(100vh - 108px); }
.generator-form { padding-top: 15px; }

/* 工作区样式 */
.main-workspace { padding: 20px; background-color: #f0f2f5; }
.v-md-editor { box-shadow: none !important; border: 1px solid var(--el-border-color) !important; }
.v-md-editor__toolbar { border-bottom: 1px solid var(--el-border-color) !important; }

/* 历史记录项样式 */
.history-item .el-card__body { padding: 15px; }
.history-item > div > div { cursor: pointer; }
.history-title { margin: 0 0 5px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.history-time { font-size: 12px; color: var(--el-text-color-secondary); margin: 0; }
.history-actions { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; }

/* 测试结果样式 */
.result {
  margin-top: 10px; padding: 10px; border-radius: 4px;
  font-family: 'Courier New', Courier, monospace; font-size: 12px;
  white-space: pre-wrap; word-break: break-all;
  background-color: #f4f4f5;
  color: #909399;
}
.result.success { background-color: #f0f9eb; color: #67c23a; }
.result.error { background-color: #fef0f0; color: #f56c6c; }

/* 素材库样式 */
.knowledge-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}
.knowledge-item p { margin: 0; flex-grow: 1; margin-right: 10px; font-size: 14px; }
</style>